package com.pc.tas.learning.trace.index;

import com.pc.cloud.services.QueryBuilder;
import com.pc.cloud.services.SearchArgs;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.*;
import org.apache.lucene.util.Version;

import java.util.Map;


public class TraceQueryBuilder implements QueryBuilder {
    private Analyzer analyzer ;
    private static TraceQueryBuilder builder;
    
    private TraceQueryBuilder(){
    	analyzer = new StandardAnalyzer(Version.LUCENE_30);
    }
    
    public synchronized static TraceQueryBuilder getInstance(){
    	if(builder != null) {
    		return builder;
    	} else {
    		builder = new TraceQueryBuilder();
    		return builder;
    	}
    }
    
    public Query buildQuery(SearchArgs traceSearchArgs) throws Exception {
        TraceSearchArgs args = (TraceSearchArgs)traceSearchArgs;
        BooleanQuery resultQuery = new BooleanQuery();
        
        if (resultQuery.clauses().size() <= 0) {
            resultQuery.add(new MatchAllDocsQuery(), BooleanClause.Occur.MUST);
        }
        
        return resultQuery;
    }

	public Query buildFilter(SearchArgs args) throws Exception {
		TraceSearchArgs traceArgs = (TraceSearchArgs)args;
		BooleanQuery resultQuery = new BooleanQuery();
		this.filterById(traceArgs,resultQuery);
		this.filterType(traceArgs,resultQuery);
		this.filterDate(traceArgs,resultQuery);		
		this.filterByOtherParams(traceArgs, resultQuery);
		this.filterByOtherRanges(traceArgs, resultQuery);
		this.filterByHour(traceArgs, resultQuery);
        if (resultQuery.clauses().size() <= 0) {
            resultQuery = null;
        }
		return resultQuery;
	}
	
	private void filterByOtherRanges(TraceSearchArgs args, BooleanQuery booleanQuery) throws Exception {
        if (args.getOtherRange() != null && args.getOtherRange().size() > 0) {
            for (Map.Entry<String, String[]> entry : args.getOtherRange().entrySet()) {
                Query query = new TermRangeQuery(entry.getKey(), entry.getValue()[0], entry.getValue()[1], true, true);
                booleanQuery.add(query, BooleanClause.Occur.MUST);
            }
        }
    }
	
	private void filterByOtherParams(TraceSearchArgs args, BooleanQuery booleanQuery) throws Exception {
        if (args.getOtherParams() != null && args.getOtherParams().size() > 0) {
            for (Map.Entry<String, String> entry : args.getOtherParams().entrySet()) {
                Query query = new TermQuery(new Term(entry.getKey(),entry.getValue()));
                booleanQuery.add(query, BooleanClause.Occur.MUST);
            }
        }
    }

	public Sort buildSort(SearchArgs args) throws Exception {
		Sort sort = null;
		if(args.getSortFields() != null) {
			sort = new Sort(args.getSortFields());
		}
		return sort;
	}

    private void filterById(TraceSearchArgs args, BooleanQuery booleanQuery) throws Exception {
        if (StringUtils.isNotBlank(args.getId())) {
            Query query = new TermQuery(new Term(TraceSearchFields.Keyword.ID, args.getId()));
            booleanQuery.add(query, BooleanClause.Occur.MUST);
        }
    }

    private void filterType(TraceSearchArgs args, BooleanQuery booleanQuery) throws Exception {
        if (StringUtils.isNotBlank(args.getType())) {
            Query query = new TermQuery(new Term(TraceSearchFields.Keyword.TYPE, args.getType()));
            booleanQuery.add(query, BooleanClause.Occur.MUST);
        }
    }
    
    private void filterByHour(TraceSearchArgs args,BooleanQuery booleanQuery) {
		if (StringUtils.isNotBlank(args.getHour())) {
            Query query = new TermQuery(new Term(TraceSearchFields.Keyword.HOUR, args.getHour()));
            booleanQuery.add(query, BooleanClause.Occur.MUST);
        }
	}

    private void filterDate(TraceSearchArgs args, BooleanQuery booleanQuery) throws Exception {
    	String beginOnStr = null;
        if (args.getBeginDate() != null) {
            beginOnStr = TraceSearchFields.dateFormat.format(args.getBeginDate());
        }
        String endOnStr = null;
        if (args.getEndDate() != null) {
            endOnStr = TraceSearchFields.dateFormat.format(args.getEndDate());
        }

        if (beginOnStr == null && endOnStr != null) {
            TermRangeQuery range = new TermRangeQuery(TraceSearchFields.Keyword.TIME, null, endOnStr, false, true);
            booleanQuery.add(range, BooleanClause.Occur.MUST);

        }
        else if (beginOnStr != null && endOnStr == null) {
            TermRangeQuery range = new TermRangeQuery(TraceSearchFields.Keyword.TIME, beginOnStr, null, true, false);
            booleanQuery.add(range, BooleanClause.Occur.MUST);

        }
        else if (beginOnStr != null && endOnStr != null) {
            TermRangeQuery range = new TermRangeQuery(TraceSearchFields.Keyword.TIME, beginOnStr, endOnStr, true, true);
            booleanQuery.add(range, BooleanClause.Occur.MUST);
        }
    }

    public Analyzer getAnalyzer() {
        return analyzer;
    }

    public void setAnalyzer(Analyzer analyzer) {
        this.analyzer = analyzer;
    }
}
